;shell.asm
[bits 32]
[global .do_shutdown]
; 常量定义
ATA_BASE         equ 0x1F0
ATA_DRIVE_SELECT equ 0x1F6
ATA_STATUS       equ 0x1F7
ATA_CMD          equ 0x1F7
ATA_DATA         equ 0x1F0
extern scroll_screen
[section .data]
; Shell界面
msg db "[root@Plain]-(/)# ", 0
cmd_buffer times 80 db 0

; 命令定义
cmd_echo db "echo", 0
cmd_help db "help", 0
cmd_ls   db "ls", 0
cmd_cat  db "cat", 0
cmd_write db "write", 0
cmd_clear db "clear", 0
cmd_shutdown db "shutdown", 0

cmd_time db "time", 0
time_str db "HH:MM:SS", 0

shutdown_msg db "Shutting down system...", 0

; 帮助信息
help_msg1 db "Available commands:", 0
help_msg2 db "  echo <message> - Display message", 0
help_msg3 db "  help          - Show this help", 0
help_msg4 db "  ls            - List files", 0
help_msg5 db "  cat <file>    - Show file content", 0
help_msg6 db "  write <file> > <content> - Write to file", 0
help_msg7 db "  clear         - Clear screen", 0
help_msg8 db "  shutdown      - Shutdown system", 0
help_msg9 db "  time          - Show current time", 0

; 错误和信息消息
not_msg db "Error: Command not found: ", 0
error_msg db "ERROR: Disk operation failed", 0
dir_entry db "  [DIR] ", 0
no_file_msg db "File not found: ", 0
write_success db "Write successful", 0
write_fail db "Write failed", 0
invalid_format_msg db "Invalid write format. Use: write filename > content", 0

; 文件系统参数
files_per_sector equ 16  ; FAT12根目录每扇区16个条目

[section .bss]
; 磁盘缓冲区
bpb_buf    resb 512
fat_buf    resb 512

; 文件系统参数
part_start resd 1
fat_start  resd 1
root_start resd 1
root_sectors resd 1

; ==== 文件系统常量定义 ====
ROOT_DIR_SECTORS    equ 14      ; 根目录占用扇区数
ROOT_DIR_START      equ 19      ; 根目录起始扇区
FAT1_START          equ 1       ; FAT1起始扇区
DATA_START          equ 33      ; 数据区起始扇区 = 1 + 9*2 + 14

; ==== 目录条目结构 ====
struc DIR_ENTRY
    .name      resb 8
    .ext       resb 3
    .attr      resb 1
    .reserved  resb 10
    .time      resw 1
    .date      resw 1
    .cluster   resw 1
    .size      resd 1
endstruc

[section .text]
extern print_str, put_char, get_key, clear_screen, fs_list_files, fs_files_count, fs_read_file 

global shell
shell:
    cmp ebx, 25
    ja .scroll
    mov ecx, 0
    mov esi, msg
    mov ah, 0x0F
    call print_str
    
    ; 初始化命令缓冲区
    mov edi, cmd_buffer
    mov ecx, 18          ; 从第18列开始输入
    mov byte [edi], 0    ; 清空缓冲区
    
.input_loop:
    call get_key
    test al, al
    jz .input_loop

    ; 处理回车
    cmp al, 0x0A
    je .execute

    ; 处理退格
    cmp al, 0x08
    je .backspace

    ; 存储并显示字符
    mov [edi], al
    inc edi
    mov ah, 0x0F
    call put_char
    inc ecx
    jmp .input_loop

.backspace:
    ; 退格处理
    cmp edi, cmd_buffer
    je .input_loop       ; 忽略空退格
    dec edi
    dec ecx
    mov al, ' '
    mov ah, 0x0F
    call put_char
    jmp .input_loop
    
.scroll:
    call scroll_screen
    dec ebx
    jmp shell
.execute:
    ; 添加字符串结束符
    mov byte [edi], 0
    
    ; 检查空命令
    mov esi, cmd_buffer
    call is_empty
    je .empty_cmd
    
    ; 跳过前导空格
    call skip_spaces
    test al, al
    jz .empty_cmd
    
    ; 检查help命令
    mov edi, cmd_help
    call cmd_cmp
    je .show_help

    ; 检查echo命令
    mov edi, cmd_echo
    call cmd_cmp
    je .do_echo
    
    
    mov edi, cmd_time
    call cmd_cmp
    je do_time

    ; 检查clear命令
    mov edi, cmd_clear
    call cmd_cmp
    je .do_clear

    ; 检查shutdown命令
    mov edi, cmd_shutdown
    call cmd_cmp
    je .do_shutdown

    ; 未知命令处理
    inc ebx
    mov ecx, 0
    mov esi, not_msg
    mov ah, 0x0C        ; 红色错误信息
    call print_str
    
    ; 只显示命令部分(第一个空格前的内容)
    mov esi, cmd_buffer
    call print_command_part
    
    inc ebx
    jmp shell

.empty_cmd:
    inc ebx
    mov ecx, 0
    jmp shell

.show_help:
    ; 显示帮助信息
    inc ebx
    mov ecx, 0
    mov esi, help_msg1
    mov ah, 0x0A        ; 绿色帮助信息
    call print_str
    
    inc ebx
    mov ecx, 0
    mov esi, help_msg2
    call print_str
    
    inc ebx
    mov ecx, 0
    mov esi, help_msg3
    call print_str
    
    inc ebx
    mov ecx, 0
    mov esi, help_msg4
    call print_str
    
    inc ebx
    mov ecx, 0
    mov esi, help_msg5
    call print_str
    
    inc ebx
    mov ecx, 0
    mov esi, help_msg6
    call print_str
    
    inc ebx
    mov ecx, 0
    mov esi, help_msg7
    call print_str
    
    inc ebx
    jmp shell

.do_echo:
    ; 跳过"echo"和后续空格
    add esi, 4
    call skip_spaces
    test al, al
    jz .no_args1         ; 无参数情况
    
    ; 显示echo参数
    inc ebx
    mov ecx, 0
    mov ah, 0x0F
    call print_str
    
.no_args1:
    inc ebx             ; 换行
    jmp shell

; === clear命令实现 ===
.do_clear:
    call clear_screen
    mov ebx, 0
    mov ecx, 0
    jmp shell
; === shutdown命令实现 ===
.do_shutdown:
    ; 显示关机消息
    inc ebx
    mov ecx, 0
    mov esi, shutdown_msg
    mov ah, 0x0C        ; 红色文字
    call print_str
    
    ; 延迟一段时间
    mov ecx, 0x5FFFFFF
.delay_loop:
    nop
    loop .delay_loop
    
    ; 尝试通过APM关机
    mov ax, 0x5307
    mov bx, 0x0001
    mov cx, 0x0003
    int 0x15
    
    ; 如果APM失败，尝试其他方法
    ; 方法1: 写0x604端口 (QEMU)
    mov dx, 0x604
    mov ax, 0x2000
    out dx, ax
    
    ; 方法2: 写0x8900端口 (BIOS)
    mov ax, 0x5307
    mov bx, 0x0001
    mov cx, 0x0003
    int 0x15
    ;方法3：使用ACPI关机
    mov eax, 0x604
    mov edx, 0x2000
    out dx, eax
    ; 如果所有方法都失败，则挂起，等待手动关机
    cli
    hlt
    jmp $
; === 辅助函数 ===

; 打印命令部分(第一个空格前的内容)
print_command_part:
    pusha
    mov ecx, 26         ; 错误信息后位置
.loop:
    lodsb
    test al, al
    jz .done
    cmp al, ' '
    je .done
    mov ah, 0x0F
    call put_char
    inc ecx
    jmp .loop
.done:
    popa
    ret

; 检查字符串是否为空或只有空格
is_empty:
    push esi
.loop:
    lodsb
    cmp al, ' '
    je .loop
    test al, al
    pop esi
    ret

; 跳过字符串中的空格
skip_spaces:
    lodsb
    cmp al, ' '
    je skip_spaces
    dec esi             ; 回退到第一个非空格字符
    ret

; 命令比较函数
cmd_cmp:
    pusha
.compare:
    mov al, [esi]
    mov bl, [edi]
    
    ; 检查命令是否结束(空格或字符串结束)
    cmp al, ' '
    je .check_cmd_end
    test al, al
    jz .check_cmd_end
    
    ; 转换为小写比较
    cmp al, 'A'
    jb .no_change1
    cmp al, 'Z'
    ja .no_change1
    add al, 0x20
.no_change1:
    cmp bl, 'A'
    jb .no_change2
    cmp bl, 'Z'
    ja .no_change2
    add bl, 0x20
    
.no_change2:
    cmp al, bl
    jne .not_equal
    inc esi
    inc edi
    jmp .compare
    
.check_cmd_end:
    ; 检查命令字符串是否也结束了
    cmp byte [edi], 0
    jne .not_equal
    
.equal:
    popa
    xor eax, eax  ; ZF=1
    ret
    
.not_equal:
    popa
    or eax, 1     ; ZF=0
    ret

; 显示固定数量的字符
print_nchars:
    pusha
    mov ah, 0x0F
.loop:
    lodsb
    call put_char
    loop .loop
    popa
    ret

print_hex:
    pushad
    mov ecx, 8
.loop:
    rol eax, 4
    mov ebx, eax
    and ebx, 0x0f
    mov bl, [hex_chars + ebx]
    mov ah, 0x0F
    call put_char
    loop .loop
    popad
    ret

do_time:
    call get_time
    inc ebx             ; 换行
    mov ecx, 0
    mov esi, time_str
    mov ah, 0x0F        ; 白色文字
    call print_str
    jmp shell
; === shutdown命令实现 ===



get_time:
    pushad

    ; 禁用NMI并读取小时
    mov al, 0x04        ; 小时寄存器
    or al, 0x80         ; 禁用NMI
    out 0x70, al
    in al, 0x71
    call bcd_to_ascii
    mov [time_str], dh
    mov [time_str+1], dl

    ; 读取分钟
    mov al, 0x02
    or al, 0x80
    out 0x70, al
    in al, 0x71
    call bcd_to_ascii
    mov [time_str+3], dh
    mov [time_str+4], dl

    ; 读取秒
    mov al, 0x00
    or al, 0x80
    out 0x70, al
    in al, 0x71
    call bcd_to_ascii
    mov [time_str+6], dh
    mov [time_str+7], dl

    popad
    ret

bcd_to_ascii:
    ; 将AL中的BCD码转换为两个ASCII字符，存储在DH和DL中
    mov dh, al
    shr dh, 4
    add dh, '0'
    mov dl, al
    and dl, 0x0F
    add dl, '0'
    ret

; === 数据区 ===
[section .data]
no_files_msg db "No files found", 0
cat_usage_msg db "Usage: cat <filename>", 0

; 文件系统参数（在Shell初始化时设置）
data_start      dd 33   ; 1+9*2+14=33
root_dir_sectors dd 14  ; 根目录扇区数
fat1_start      dd 1    ; FAT1起始扇区
file_cluster dw 0
free_entry   dd 0
lba_low      db 0
lba_mid      db 0
lba_high     db 0

sectors_per_track dd 18
num_heads        dd 2
boot_drive       db 0
hex_chars db '0123456789ABCDEF'